package NIO.none_blocking;
/*
 * @Author  Wrial
 * @Date Created in 11:09 2020/4/11
 * @Description 使用nio包下类创建非阻塞的Server
 */


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class NoneBlockingServer {
    public static void main(String[] args) throws IOException {
        new NoneBlockingServer(8090).service();
    }

    private ServerSocketChannel serverSocketChannel = null;
    private int port;
    private Selector selector;
    private Charset charset;

    // 初始化ServerSocketChannel和选择器,并配置非阻塞
    public NoneBlockingServer(int port) throws IOException {
        this.port = port;
        charset = Charset.forName("UTF-8");
        serverSocketChannel = ServerSocketChannel.open();
        selector = Selector.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8090));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().setReuseAddress(true);
        // 在选择器进行事件的注册
        System.out.println("server init ");

    }

    public void service() throws IOException {
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (selector.select() > 0) {

            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectionKeys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                it.remove();
                // 对各种状态进行处理
                try {
                    handle(key);
                } catch (IOException e) {
                    e.printStackTrace();
                    // 只有在出现异常的时候进行关闭
                    try {
                        if (key != null) {
                            key.cancel();
                            key.channel().close();
                        }
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }

            }
        }
    }

    private void handle(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            handleAccept(key);
        }
        if (key.isReadable()) {
            receive(key);
        }
        if (key.isWritable()) {
            send(key);
        }
    }

    private void send(SelectionKey key) throws IOException {
//        System.out.println("writable  send");
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        // limit = position position = 0  因为读已经到了limit了，需要进行覆盖写
        buffer.flip();

        String data = decode(buffer);

        // 未到一行 就是无效数据
        if (data.indexOf("\r\n") == -1) {
            return;
        }
        // 截取一行
        String outputData = data.substring(0, data.indexOf("\n") + 1);
        System.out.println("收到的数据 ：" + outputData);
        // 按照编码规则进行编码
        ByteBuffer outputBuffer = encode("echo : " +outputData);
        while (outputBuffer.hasRemaining()) {
            socketChannel.write(outputBuffer);
        }
        // 更新buffer的position
        ByteBuffer temp = encode(outputData);
        buffer.position(temp.limit());
        // 删除buffer已经处理过的数据
        buffer.compact();
        if (outputData.equals("end\r\n")) {
            key.channel();
            socketChannel.close();
            System.out.println("连接关闭");
        }
    }

    private ByteBuffer encode(String data) {
        return charset.encode( data);
    }

    private String decode(ByteBuffer buffer) {
        CharBuffer charBuffer = charset.decode(buffer);
        return charBuffer.toString();
    }

    // 如果没有读完就还会继续触发此事件
    private void receive(SelectionKey key) throws IOException {
        System.out.println("readable");

        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();

        // 因为在非阻塞中不一定一次就讲数据全部读取进来
        ByteBuffer readBuffer = ByteBuffer.allocate(64);
        socketChannel.read(readBuffer);
        // limit = position position = 0
        readBuffer.flip();
        buffer.limit(buffer.capacity());
        // 将tempBuffer内容拷贝到buffer
        buffer.put(readBuffer);

    }

    private void handleAccept(SelectionKey key) throws IOException {

        System.out.println("acceptable");
        ServerSocketChannel sSC = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = sSC.accept();
        System.out.println("接收到客户端的连接请求 ：" + socketChannel.getLocalAddress());
        socketChannel.configureBlocking(false);
        // 连接到了下一步可能就要进行数据的读
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 给客户的注册读写监听器,并将Buffer关联给这个Channel，下次用的时候直接attach
        socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, buffer);

    }


}
